#include <linux/module.h>
#include <linux/init.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>

struct gpio_key_data {
    struct input_dev *input;
    int gpio;
    int irq;
    int last_state;
};

static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
    struct gpio_key_data *data = dev_id;
    int state = gpio_get_value(data->gpio);
    
    if (state != data->last_state) {
        input_sync(data->input); //向用户空间上报
        data->last_state = state;
    }
    
    return IRQ_HANDLED;
}

static int gpio_key_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct gpio_key_data *data;
    int ret;

    // 分配内存
    data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    if (!data)
        return -ENOMEM;

    // 获取 GPIO 号
    data->gpio = of_get_named_gpio(dev->of_node, "key-gpio", 0);
    if (data->gpio < 0)
        return dev_err_probe(dev, data->gpio, "failed to get gpio\n");

    // 驱动中要想使用某一个gpio，就必须先调用gpio_request接口来向内核申请，得到允许后才可以去使用这个gpio
    ret = devm_gpio_request(dev, data->gpio, "mykeys");
    if (ret)
        printk("gpio request failed\n");

    // 配置为输入模式
    ret = gpio_direction_input(data->gpio);
    if (ret)
        printk("gpio direction failed\n");

    // 初始化输入设备
    data->input = devm_input_allocate_device(dev);
    if (!data->input)
        return -ENOMEM;

    data->input->name = "GPIO Key";
    data->input->phys = "mykeys/input0";
    data->input->id.bustype = BUS_HOST;

    // 设置按键事件
    set_bit(EV_KEY, data->input->evbit);
    set_bit(KEY_POWER, data->input->keybit);

    // 注册输入设备
    ret = input_register_device(data->input);
    if (ret)
        printk("input register failed\n");

    // 获取中断号
    data->irq = gpio_to_irq(data->gpio);
    if (data->irq < 0)
       printk("gpio_to_irq failed\n");

    // 注册中断处理
    ret = devm_request_irq(dev, data->irq, gpio_key_isr,//中断回调函数 gpio_key_isr
                          IRQF_TRIGGER_LOW, //低电平触发
                          "mykeys", data);
    if (ret)
        printk("request irq failed\n");

    // 初始化状态
    data->last_state = gpio_get_value(data->gpio);

    return 0;
}

static const struct of_device_id gpio_key_of_match[] = {
    { .compatible = "mykeys" },
    {},
};
MODULE_DEVICE_TABLE(of, gpio_key_of_match);

static struct platform_driver gpio_key_driver = {
    .driver = {
        .name = "mykeys",
        .of_match_table = gpio_key_of_match,
    },
    .probe = gpio_key_probe,
};
module_platform_driver(gpio_key_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Huangxy");
MODULE_DESCRIPTION("GPIO Key Driver");